package ai.people.core.security.config;

import ai.people.core.security.entity.IgnoreUrls;
import ai.people.core.security.properties.ResourceServiceProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Import;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.authentication.TokenExtractor;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport;

/**
 * 资源服务器配置
 *
 * @author yuanqinglong
 * @date 2022/4/16 16:39
 */
@EnableResourceServer
@RequiredArgsConstructor
@Import({SecurityProblemSupport.class, CustomizeTokenExtractor.class})
@EnableConfigurationProperties(ResourceServiceProperties.class)
public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {


    /**
     * redis令牌存储
     */
    private final TokenStore redisTokenStore;

    /**
     * 安全问题支持
     */
    private final SecurityProblemSupport securityProblemSupport;

    /**
     * 资源服务属性
     */
    private final ResourceServiceProperties resourceServiceProperties;

    /**
     * token提取策略
     */
    private final TokenExtractor cookieBearerTokenExtractor;

    /**
     * 配置
     *
     * @param resources 资源
     * @throws Exception 异常
     */
    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources
                // 认证异常处理、调用统一异常处理程序
                .authenticationEntryPoint(securityProblemSupport)
                // 无权限访问异常处理、调用统一异常处理程序
                .accessDeniedHandler(securityProblemSupport)
                // 通过redis校验token
                .tokenStore(redisTokenStore)
                // 提取token策略
                .tokenExtractor(cookieBearerTokenExtractor)
                .resourceId(resourceServiceProperties.getResourceId())
                // 无状态
                .stateless(true);

    }


    /**
     * 配置
     *
     * @param http http
     * @throws Exception 异常
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .formLogin(AbstractHttpConfigurer::disable)
                .exceptionHandling(exp ->
                        exp.authenticationEntryPoint(securityProblemSupport)
                                .accessDeniedHandler(securityProblemSupport))
                //配置无需授权访问的接口和需要令牌可以访问的接口.
                .authorizeRequests(this.configureAuthorizeRequestsCustomizer())
                //表示资源服务器的该请求 需要令牌有读写权限
                //.authorizeRequests().antMatchers("/**").access("#oauth2.hasScope('all')")
                .csrf(AbstractHttpConfigurer::disable);
    }

    /**
     * 配置授权请求编辑器
     *
     * @return {@link Customizer}<{@link Customizer<ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry>
     */
    private Customizer<ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry> configureAuthorizeRequestsCustomizer() {
        return authentication -> {
            // 配置忽略验证的url
            for (String ignoreUrl : resourceServiceProperties.getIgnoreUrls()) {
                IgnoreUrls ignoreUrls = resourceServiceProperties.processingUrls(ignoreUrl);
                authentication.antMatchers(ignoreUrls.getHttpMethod(), ignoreUrls.getUrl()).permitAll();
            }
            // 除忽略的url外其他请求需要认证校验
            authentication.anyRequest().authenticated();
        };
    }


}
